package com.example.bootintegration.datasource;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Quintoy
 * @date 12/30/22
 */
public class DataSourceRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        registerIfAbsent(registry);
    }

    public static Set<String> registerIfAbsent(BeanDefinitionRegistry registry) {
        DefaultListableBeanFactory bf = (DefaultListableBeanFactory)registry;
        Environment env = bf.getBean(ConfigurableEnvironment.class);
        Set<String> dataSourceNames = configGroupOf("boot.datasource.", ".url", env);
        dataSourceNames.forEach(dataSource -> {
            if (!bf.containsBeanDefinition(dataSource + "JdbcTemplate")) {
                ConfigurableDataSource configurableDataSource = new ConfigurableDataSource(dataSource, env);
                bf.registerBeanDefinition(dataSource + "JdbcTemplate", BeanDefinitionBuilder.genericBeanDefinition(JdbcTemplate.class)
                        .addConstructorArgValue(configurableDataSource.getTargetDataSource()).getBeanDefinition());
            }
        });

        return dataSourceNames;
    }

    private static Set<String> configGroupOf(String prefix, String suffix, Environment environment) {
        Set<String> result = new LinkedHashSet<>();
        String regEx = String.format("%s([^.]+)%s",
                prefix.replaceAll("\\.", "\\\\."),
                suffix.replaceAll("\\.", "\\\\."));
        Pattern pattern = Pattern.compile(regEx);
        for (PropertySource<?> propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) {
            if (propertySource instanceof EnumerablePropertySource) {
                result.addAll(configGroupOf(propertySource, pattern));
            }
        }
        return result;
    }
    private static Set<String> configGroupOf(PropertySource<?> propertySource, Pattern pattern) {
        Set<String> result = new LinkedHashSet<>();
        for (String key : ((EnumerablePropertySource<?>) propertySource).getPropertyNames()) {
            Matcher matcher = pattern.matcher(key);
            if (matcher.find()) {
                result.add(matcher.group(1));
            }
        }
        return result;
    }


}
